//---------------------------------------------------------------------------
// Thread Class
// Re-writer : Woo-Hyun Kim
// Email : woorung@icu.ac.kr
// Date : Jul. 7, 2006
//
// Note. this class is referred in http://joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Advanced/ThreadClass
//---------------------------------------------------------------------------
#include "CThread.h"
//---------------------------------------------------------------------------
// static variables
long CThread::_id = 0;
pthread_attr_t CThread::_attr;
//---------------------------------------------------------------------------
void* CThread::threadHandler(void* arg)
{
	struct sigaction act;
	act.sa_handler = interruptHandler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;

	int state = sigaction(SIGUSR1, &act, NULL);
	if(state!=0) {
		cerr << "sigaction() error";
		exit(-1);
	}

	sigset_t newmask;
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGUSR1);

	pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);


	CThread* thread = (CThread*) arg;

	try {
		thread->run();
	} catch (InterruptedException& e) {
		thread->_isInterrupted = true;
		cout << thread->getName() << " interrupted" << endl;
	} catch (...) {
		// release lock after processing thread
		pthread_mutex_lock(&thread->_mutex);

		thread->_state = -1;

		if(thread->_joinning) {
			pthread_cond_broadcast(&thread->_cond);
		}

		pthread_mutex_unlock(&thread->_mutex);

		throw;
	}

	// release lock after processing thread
	pthread_mutex_lock(&thread->_mutex);

	thread->_state = -1;

	if(thread->_joinning) {
		pthread_cond_broadcast(&thread->_cond);
	}
	else {
	}

	pthread_mutex_unlock(&thread->_mutex);

	return NULL;
}
//---------------------------------------------------------------------------
void CThread::interruptHandler(int sigInt)
{
	throw InterruptedException("Thread interrupted");    
}
//---------------------------------------------------------------------------
CThread::CThread()
{
	char name[32];
	sprintf(name, "Thread-%ld", _id);
	char const *pname = name;

	string thr_name = pname;

	init(thr_name);
}
//---------------------------------------------------------------------------
CThread::CThread(const string& name)
{
	init(name);
}
//---------------------------------------------------------------------------
CThread::~CThread()
{
	pthread_attr_destroy(&_attr);
	pthread_mutex_destroy(&_mutex);
	pthread_cond_destroy(&_cond);
}
//---------------------------------------------------------------------------
void CThread::init(const string& name)
{
	_name = name;
	_joinning = false;
	_isInterrupted = false;

	pthread_mutex_init(&_mutex, NULL);
	pthread_cond_init(&_cond, NULL);

	_state = 0;

	// lock for generating thread id
	pthread_mutex_lock(&_mutex);

	if(_id==0) {
		// not to "join" the generated thread
		pthread_attr_init(&_attr);
		pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_DETACHED);

		// allow to cancle the running thread
		//pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
		//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
	}

	_id++;    

	pthread_mutex_unlock(&_mutex);
}
//---------------------------------------------------------------------------
void CThread::start()
{
	if (_state == 1) {
		throw IllegalThreadStateException("Thread already started");
	}
	else if (_state == 0) {
		_state = 1;
		pthread_create(&_thread, &_attr, threadHandler, this);
	}
	else if (_state == -1) {
		throw IllegalThreadStateException("Thread had been started");
	}
}
//---------------------------------------------------------------------------
void CThread::sleep(time_t sec)
{
	// ignore nano seconds
	long nsec = 0;

	struct timespec req;
	struct timespec rem;

	if ((sec==0) && (nsec==0)) {
		req.tv_sec=0;
		req.tv_nsec=999999999L; // 999 999 999
	}
	else {
		req.tv_sec=sec;
		req.tv_nsec=nsec; // 999 999 999
	}

	//rem.tv_sec=0;
	//rem.tv_nsec=0;

	//while (nanosleep(&req, &rem))
	while(nanosleep(&req, &req)) {
		if(errno != EINTR) break;
	}

	return;
}
//---------------------------------------------------------------------------
void CThread::join()
{
	pthread_mutex_lock(&_mutex);

	_joinning = true;

	if(_state==1) {
#ifdef DEBUG
cerr << "===>wait : " << getName() << endl;
#endif
		pthread_cond_wait(&_cond, &_mutex);
#ifdef DEBUG
cerr << "<===wait : " << getName() << endl;
#endif
	}

	_joinning = false;

	pthread_mutex_unlock(&_mutex);

}
//---------------------------------------------------------------------------
//void CThread::join(time_t sec)
void CThread::join(unsigned long time)
{
	pthread_mutex_lock(&_mutex);

	_joinning = true;

	if(_state==1) {
		//struct timespec timeout;
		//timeout.tv_sec = sec;
		//timeout.tv_nsec = 0;

		struct timeval now;
		struct timespec timeout;

		gettimeofday(&now, NULL);

		ldiv_t t = ldiv(time * 1000000, 1000000000);
		timeout.tv_sec = now.tv_sec + t.quot;
		timeout.tv_nsec = now.tv_usec * 1000 + t.rem;

		pthread_cond_timedwait(&_cond, &_mutex, &timeout);
	}

	_joinning = false;

        pthread_mutex_unlock(&_mutex);
}
//---------------------------------------------------------------------------
void CThread::interrupt()
{
	pthread_kill(_thread, SIGUSR1);
}
//---------------------------------------------------------------------------
bool CThread::isInterrupted()
{
	return _isInterrupted;
}
//---------------------------------------------------------------------------
bool CThread::isAlive()
{
	return _state == 1;
}
//---------------------------------------------------------------------------
char *CThread::getName()
{
	return (char *)_name.c_str();
}
//---------------------------------------------------------------------------
void CThread::setName(char *name)
{
	_name = name;
}
//--------------------------------------------------------------------------
